package org.wheel.plugins.toolkit.sql.core;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.db.sql.Direction;
import org.wheel.plugins.toolkit.sql.capability.*;
import org.wheel.plugins.toolkit.sql.capability.impl.LambdaSubCollectionImpl;
import org.wheel.plugins.toolkit.sql.driver.JsqlParserDriver;
import org.wheel.plugins.toolkit.sql.entity.BeanMeta;
import org.wheel.plugins.toolkit.sql.enums.CompareType;
import org.wheel.plugins.toolkit.sql.enums.LogicalOp;
import org.wheel.plugins.toolkit.sql.function.FunctionBuilder;
import org.wheel.plugins.toolkit.sql.helper.ColumnRef;
import org.wheel.plugins.toolkit.sql.helper.FieldMeta;
import org.wheel.plugins.toolkit.sql.helper.LambdaUtils;
import org.wheel.plugins.toolkit.sql.helper.SFunction;
import org.wheel.plugins.toolkit.sql.segment.*;
import org.wheel.plugins.toolkit.sql.segment.having.HavingCondition;
import org.wheel.plugins.toolkit.sql.segment.having.HavingElement;
import org.wheel.plugins.toolkit.sql.segment.having.HavingGroup;

import java.util.*;
import java.util.function.Consumer;

/**
 * Lambda 查询包装器 – 完整链式实现
 */
public class LambdaQueryWrapper<T> extends AbstractWrapper<T, LambdaQueryWrapper<T>>
        implements LambdaAggregate<LambdaQueryWrapper<T>,T>,
        LambdaWindow<LambdaQueryWrapper<T>, T>, LambdaCollection<LambdaQueryWrapper<T>,T>,
        LambdaOrder<LambdaQueryWrapper<T>,T>, LambdaHaving<LambdaQueryWrapper<T>, T>,
        LambdaPage<LambdaQueryWrapper<T>, T> {

    private final Map<String, Object> paramMap = new LinkedHashMap<>();


    private String prefixParentPath = null;

    public LambdaQueryWrapper(Class<T> clazz) {
        super(clazz);
    }

    public LambdaQueryWrapper(Class<T> clazz, String tableAlias) {
        super(clazz, tableAlias);
    }

    /* ========== SELECT ========== */

    public LambdaQueryWrapper<T> selectAll() {
        List<FieldMeta> fieldMetas = BeanMeta.of(this.clazz).getFields();
        List<SqlSegment> refs = fieldMetas.stream()
                .map(m->ColumnRef.of(this.mainTableAlias, m.getColumn(), m.getFieldName())
                        .withWrapper(this).toSqlSegment()).toList();
        tree.select(refs);
        return this;
    }

    public LambdaQueryWrapper<T> select(SqlSegment... segments) {
        tree.select(segments);
        return this;
    }

    @SafeVarargs
    public final <J> LambdaQueryWrapper<T> select(SFunction<J, ?>... columns) {
        for (SFunction<J, ?> fn : columns) {
            tree.select(ColumnRef.of(fn, this));
        }
        return this;
    }
    public <J, D, R> LambdaQueryWrapper<T> selectAs(SFunction<J, R> columns, SFunction<D, R> columnAlias) {
        return selectAs(null, columns, columnAlias);
    }

    public <J, D, R> LambdaQueryWrapper<T> selectAs(String tableAlias, SFunction<J, R> columns, SFunction<D, R> columnAlias) {
        tree.select(ColumnRef.of(columns, columnAlias, this));
        return this;
    }
    public <D, R> LambdaQueryWrapper<T> selectAs(SqlSegment columns,  SFunction<D, R> columnAlias){
        String colAlias = null;
        if(columnAlias != null) {
            colAlias = LambdaUtils.getFieldName(columnAlias);
        }
        tree.select(ColumnRef.of(null, columns.getSql(), colAlias, this));
        return this;
    }
    public LambdaQueryWrapper<T> selectAs(FunctionBuilder fnc){
        tree.select(fnc.toSegment());
        return this;
    }
    public <D, R> LambdaQueryWrapper<T> selectAs(FunctionBuilder fnc,  SFunction<D, R> columnAlias){
        tree.select(fnc.as(columnAlias).toSegment());
        return this;
    }
    public <J> LambdaQueryWrapper<T> selectAs(String tableAlias, SFunction<J, ?> columns, String columnAlias) {
        ColumnRef ref = ColumnRef.of(columns, this).withTableAlias(tableAlias).as(columnAlias);
        tree.select(ref);
        return this;
    }
    public <J> LambdaQueryWrapper<T> selectAs(SFunction<J, ?> columns, String columnAlias) {
        ColumnRef ref = ColumnRef.of(columns, this).as(columnAlias);
        tree.select(ref);
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> collection(SqlSegment seg) {
        tree.select(seg);
        return self();
    }


//    @Override
//    public <A> LambdaQueryWrapper<T> selectCollection(String tableAlias, Class<?> itemClass, SFunction<A, Collection<?>> collectionFn) {
//        String field = LambdaUtils.getFieldName(collectionFn);
//        if (StrUtil.isBlank(tableAlias)) {
//            if(this.getContext().getAliasRegistry().isRegister(itemClass)) {
//                tableAlias = this.getContext().getAliasRegistry().resolveAlias(itemClass);
//            }
//        }
//        ColumnRef[] segs = SelectBuilder.collection(tableAlias, field, itemClass, this);
//        return collection(SelectBuilder.join(segs));   // ← 拼成一条
//    }

//    @Override
//    public <A> LambdaQueryWrapper<T> selectAssociation(String tableAlias, Class<?> itemClass, SFunction<A, ?> assocFn) {
//        String field = LambdaUtils.getFieldName(assocFn);
//        if(StrUtil.isBlank(tableAlias)){
//            if(this.getContext().getAliasRegistry().isRegister(itemClass)) {
//                tableAlias = this.getContext().getAliasRegistry().resolveAlias(itemClass);
//            }
//        }
//        return collection(SelectBuilder.join(SelectBuilder.collection(tableAlias, field, itemClass,  this)));
//    }

    @Override
    public <A, B, C, D extends LambdaSubCollection<D, E>, E> LambdaQueryWrapper<T>  selectCollection(String tableAlias, Class<A> collectionClass, SFunction<B, Collection<C>> collectionFn, Consumer<LambdaSubCollection<D, E>> nestedQuery) {
        String field = LambdaUtils.getFieldName(collectionFn);
        ColumnRef[] segs = SelectBuilder.collection(tableAlias, field, collectionClass, this);
        collection(SelectBuilder.join(segs));

        if(nestedQuery != null) {
            LambdaSubCollection<D, E> subCollection = new LambdaSubCollectionImpl<D, E>(field, (LambdaQueryWrapper<E>) self());
            nestedQuery.accept(subCollection);
        }

        return self();   // ← 拼成一条
    }


    @Override
    public <A, B, C, D extends LambdaSubCollection<D, E>, E> LambdaQueryWrapper<T> selectAssociation(String tableAlias, Class<A> assocClass, SFunction<B, C> assocFn, Consumer<LambdaSubCollection<D, E>> nestedQuery) {
        String field = LambdaUtils.getFieldName(assocFn);
        Class<A> targetFieldClazz = (Class<A>) LambdaUtils.getCapturingClass(assocFn);
        collection(SelectBuilder.join(SelectBuilder.collection(tableAlias, field, assocClass,  this)));

        if(nestedQuery != null) {
            LambdaSubCollection<D, E> subCollection = new LambdaSubCollectionImpl<D, E>(field, (LambdaQueryWrapper<E>) self());
            nestedQuery.accept(subCollection);
        }

        return self();
    }

    /* ========== GROUP / HAVING / ORDER ========== */
    @SafeVarargs
    public final <J> LambdaQueryWrapper<T> groupBy(SFunction<J, ?>... columns) {
        tree.groupBy(GroupBySegment.of(Arrays.stream(columns)
                .map(f -> ColumnRef.of(f, (SFunction<?, ?>) null, self()).withoutAlias())
                .toArray(SqlSegment[]::new)));
        return this;
    }

    /* ========== 规则插件 ========== */
    public LambdaQueryWrapper<T> enableRule(String ruleName) {
        tree.enableRule(ruleName);
        return this;
    }


    @Override
    public LambdaQueryWrapper<T> function(FunctionBuilder fn) {
        return select(fn.bind(this).toSegment());
    }


    /* ========== Having ========== */

    @Override
    public LambdaQueryWrapper<T> having(String sql) {
        HavingElement cond = new HavingCondition(sql);
        tree.having(mergeHaving(cond));
        return self();
    }

    @Override
    public LambdaQueryWrapper<T> havingOp(LogicalOp op, Consumer<HavingGroup> consumer) {
        HavingGroup group = new HavingGroup(op);
        consumer.accept(group);
        tree.having(mergeHaving(group));
        return self();
    }

    // 合并辅助（私有）
    private HavingElement mergeHaving(HavingElement newOne) {
        if (tree.getHavingRoot() == null) return newOne;
        return new HavingGroup(LogicalOp.AND)
                .add(tree.getHavingRoot())
                .add(newOne);
    }
    /* ========== 分页 ========== */

    /**
     * 分页
     * @param pageNo 页码，从1开始, 默认1
     * @param pageSize 每页数量
     * @return
     */
    @Override
    public LambdaQueryWrapper<T> pageLimit(int pageNo, int pageSize) {
        if(pageNo <= 0){
            pageNo = 1;
        }
        tree.page(PageSegment.of(pageNo, pageSize));
        return this;
    }

    @Override
    public LambdaQueryWrapper<T> pageCursor(SFunction<T, ?> cursorCol, Object cursorValue, AbstractWrapper<?, ?> wrapper) {
        // 1. 字段表达式
        ColumnRef ref = ColumnRef.of(cursorCol, wrapper).withoutAlias();
        // 2. 包成 WHERE 片段
        tree.where(WhereSegment.of(CompareType.GT, ref, cursorValue, this));
        // 3. 默认加 ORDER BY cursorCol ASC（游标必须有序）
        tree.orderBy(OrderBySegment.asc(ref));
        return self();
    }

    @Override
    public LambdaQueryWrapper<T> pageSeek(LogicalOp logicalOp, AbstractWrapper<?, ?> wrapper, SFunction<T, ?> col1, Object val1,
                                                SFunction<T, ?> col2, Object val2,
                                                List<Pair<SFunction<T, ?>, Object>> more) {
        // 1. 收齐所有【列 + 值】
        List<SFunction<T, ?>> cols = new ArrayList<>();
        List<Object> values = new ArrayList<>();
        cols.add(col1);
        cols.add(col2);
        values.add(val1);
        values.add(val2);
        if (CollUtil.isNotEmpty(more)) {
            for (Pair<SFunction<T, ?>, Object> sFunctionObjectPair : more) {
                cols.add(sFunctionObjectPair.getKey());
                values.add(sFunctionObjectPair.getValue());
            }
        }

        // 2. 一次性落库所有值，并拿占位符 key
        List<ColumnRef> columnRefs = cols.stream().map(c -> ColumnRef.of(c, wrapper).withoutAlias()).toList();

        // 3. 包成「多列比较」片段（> 或 >= 均可）
        MultiColumnWhereSegment seg = new MultiColumnWhereSegment(logicalOp, columnRefs, values, CompareType.GT, this);
        tree.where(seg);

        // 4. 默认加 ORDER BY 列组 ASC（Seek 必须有序）
        tree.orderBy(OrderBySegment.asc(columnRefs.toArray(SqlSegment[]::new)));
        return self();
    }

    /* ========== Order By ========== */
    @Override
    public <A, B> LambdaQueryWrapper<T> order(Direction direction, SFunction<A, B> col, String tableAlias) {
        if(Direction.DESC.equals(direction)){
            tree.orderBy(OrderBySegment.desc(ColumnRef.of(col, tableAlias).withWrapper(this).withoutAlias()));
        }else {
            tree.orderBy(OrderBySegment.asc(ColumnRef.of(col, tableAlias).withWrapper(this).withoutAlias()));
        }
        return this;
    }

    @Override
    public <A, B> LambdaQueryWrapper<T> order(Direction direction, List<Pair<SFunction<A, B>, String>> cols) {
        if(Direction.DESC.equals(direction)) {
            tree.orderBy(OrderBySegment.desc(cols.stream()
                    .map(f -> ColumnRef.of(f.getKey(), f.getValue()).withWrapper(this).withoutAlias())
                    .toArray(SqlSegment[]::new)));
        }else{
            tree.orderBy(OrderBySegment.asc(cols.stream()
                    .map(f -> ColumnRef.of(f.getKey(), f.getValue()).withWrapper(this).withoutAlias())
                    .toArray(SqlSegment[]::new)));
        }
        return this;
    }


    public String getCountSql(){
        String sql = new JsqlParserDriver(this.dialect).toSql(this.tree, true);
        return renderSql(sql);
    }

    public String getSql(){
        if(CollUtil.isEmpty(tree.getSelectList())){
            String tableAlias = this.resolveAlias(this.getEntityClass(), -1);
            tree.getSelectList().add(ColumnRef.of(tableAlias, "*", null).withoutAlias());
        }
        return super.getSql();
    }
}